﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace ClassLibrary
{
    public class BytesConverter
    {
        /// <summary>
        /// Bytes To Hex String
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string BytesToHexString(byte[] bytes)
        {
            StringBuilder sbResult = new StringBuilder();
            if (bytes != null)
            {
                sbResult.Append("0x");
                for (int i = 0; i < bytes.Length; i++)
                {
                    sbResult.Append(bytes[i].ToString("X2"));
                    sbResult.Append(" ");
                }
            }
            return sbResult.ToString();
        }

        /// <summary>
        /// Convert byte[] to string 
        /// </summary>
        /// <param name="bytes">byte source array</param>
        /// <param name="byteType">byte type</param>
        /// <returns></returns>
        public static string BytesToString(byte[] bytes, ByteType byteType)
        {
            StringBuilder sbResult;
            if (bytes == null || (bytes.Length != (int)byteType && (int)byteType != 0))
            {
                throw new Exception("The length of the source bytes is not valid!");
            }
            if (byteType == ByteType.BCD)
            {//if is mobile number
                sbResult = new StringBuilder(12);
                for (var m = 0; m < bytes.Length; m++)
                {
                    sbResult.Append(((bytes[m] << 4) >> 4).ToString() + (bytes[++m] << 4).ToString());
                }
            }
            else
            {
                sbResult = new StringBuilder();
                sbResult.Append(System.Text.Encoding.Default.GetString(bytes));
            }

            return sbResult.ToString();
        }

        /*
         *  BASE64字符表：ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
         */
        /// <summary>
        /// Convert byte[] to Base64String
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="byteType"></param>
        /// <returns></returns>
        public static string ToBase64String(byte[] bytes, ByteType byteType = 0)
        {
            try
            {
                if ((int)byteType > 0 && bytes.Length != bytes.Length)
                {
                    throw new Exception("The length of the source bytes is not valid!");
                }
                string str = Convert.ToBase64String(bytes);
                return str;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Convert string to byte[]
        /// </summary>
        /// <param name="sourceString">source string</param>
        /// <param name="byteType">result byte array</param>
        /// <returns></returns>
        public static byte[] StringToBytes(string sourceString, ByteType byteType)
        {
            byte[] bytesResult = new byte[(int)byteType];
            bytesResult = System.Text.Encoding.Default.GetBytes(sourceString);

            if (bytesResult.Length != (int)byteType)
                throw new Exception("The length of the source string is invalid!");

            return bytesResult;
        }

        /// <summary>
        /// Convert Base64String to byte[]
        /// </summary>
        /// <param name="str"></param>
        /// <param name="bytetype"></param>
        /// <returns></returns>
        public static byte[] FromBase64String(string str, ByteType bytetype = 0)
        {
            byte[] data = Convert.FromBase64String(str);
            if (bytetype > 0 && data.Length != (int)bytetype)
            {
                throw new Exception("The length of the source string is invalid!");
            }
            return data;
        }

        /// <summary>
        /// Convert string to byte[] with fixed length
        /// 定长字符串，位数不足时，右补十六进制0x00，汉字采用GBK 编码。
        /// </summary>
        /// <param name="sourceString"></param>
        /// <param name="fixedLength"></param>
        /// <returns></returns>
        public static byte[] OctetStringToBytes(string sourceString, int fixedLength)
        {
            byte[] bytesResult = new byte[fixedLength];

            byte[] buffer = System.Text.Encoding.GetEncoding("GBK").GetBytes(sourceString);

            Buffer.BlockCopy(buffer, 0, bytesResult, 0, buffer.Length > fixedLength ? fixedLength : buffer.Length);

            return bytesResult;
        }

        /// <summary>
        /// 将字节数组转换为字符串
        /// 定长字符串，位数不足时，右补十六进制0x00，汉字采用GBK 编码。
        /// </summary>
        /// <param name="sourceByte"></param>
        /// <returns></returns>
        public static string ReadOctetString(byte[] sourceByte)
        {
            return System.Text.Encoding.GetEncoding("GBK").GetString(sourceByte).Trim('\0');
        }

        /// <summary>
        /// 将字节数组转换为字符串
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <param name="fixedLength"></param>
        /// <returns></returns>
        public static string ReadOctetString(byte[] byteData, int index, int fixedLength)
        {
            byte[] buffer = new byte[fixedLength];
            Buffer.BlockCopy(byteData, index, buffer, 0, fixedLength);

            return ReadOctetString(buffer);
        }

        /// <summary>
        /// 转换为固定长字节数组
        /// </summary>
        /// <param name="source"></param>
        /// <param name="fixedLength"></param>
        public static byte[] ToFixedLengthBytes(string sourceString, int fixedLength)
        {
            byte[] bytesResult = new byte[fixedLength];

            byte[] buffer = System.Text.Encoding.Default.GetBytes(sourceString);

            Buffer.BlockCopy(buffer, 0, bytesResult, 0, buffer.Length > fixedLength ? fixedLength : buffer.Length);

            return bytesResult;
        }

        /// <summary>
        /// 将固定长字节数组转换为字符串，去掉空字符
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <param name="fixedLength"></param>
        /// <returns></returns>
        public static string ReadFixedLengthBytes(byte[] byteData, int index, int fixedLength)
        {
            byte[] buffer = new byte[fixedLength];
            Buffer.BlockCopy(byteData, index, buffer, 0, fixedLength);

            return System.Text.Encoding.Default.GetString(buffer).Trim('\0');
        }

        /// <summary>
        ///DATE 4 BYTES 日月年(dmyy)，年的表示是先将年转换成2 位十六进制数，如2009 表示为0x07 0xD9。
        ///TIME 3 BYTES 时分秒(hms)
        /// </summary>
        /// <param name="datetime"></param>
        /// <returns></returns>
        public static byte[] ToSevenBytesDateTime(DateTime datetime)
        {
            byte[] bytesResult = new byte[7];
            bytesResult[0] = (byte)(datetime.Day);         //日
            bytesResult[1] = (byte)(datetime.Month);        //月

            byte[] bytesYear = BitConverter.GetBytes(datetime.Year);
            //Array.Reverse(bytesYear);
            bytesResult[2] = bytesYear[1];
            bytesResult[3] = bytesYear[0];

            bytesResult[4] = (byte)datetime.Hour;         //时
            bytesResult[5] = (byte)datetime.Minute;      //分
            bytesResult[6] = (byte)datetime.Second;     //秒
            return bytesResult;
        }

        /// <summary>
        /// 将7字节数组转换为日期
        /// DATE 4 BYTES 日月年(dmyy)，年的表示是先将年转换成2 位十六进制数，如2009 表示为0x07 0xD9。
        /// TIME 3 BYTES 时分秒(hms)
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static DateTime ReadSevenBytesDateTime(byte[] byteData, int index)
        {
            int day = byteData[index];
            int month = byteData[index + 1];
            int year = byteData[index + 2] * 256 + byteData[index + 3];

            int hour = byteData[index + 4];
            int minute = byteData[index + 5];
            int second = byteData[index + 6];

            DateTime dt = new DateTime(year, month, day, hour, minute, second);
            return dt;
        }


        /// <summary>
        /// Convert ushort number to BigEndian byte array
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static ushort BigEndianConverter(ushort number)
        {
            byte[] temp = BitConverter.GetBytes(number);
            Array.Reverse(temp);
            return BitConverter.ToUInt16(temp, 0);
        }

        /// <summary>
        ///  Convert uint number to BigEndian byte array
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static int BigEndianConverter(int number)
        {
            byte[] temp = BitConverter.GetBytes(number);
            Array.Reverse(temp);
            return BitConverter.ToInt32(temp, 0);
        }

        /// <summary>
        /// Convert ushort number to BigEndian byte array
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static short BigEndianConverter(short number)
        {
            byte[] temp = BitConverter.GetBytes(number);
            Array.Reverse(temp);
            return BitConverter.ToInt16(temp, 0);
        }

        /// <summary>
        ///  Convert uint number to BigEndian byte array
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static uint BigEndianConverter(uint number)
        {
            byte[] temp = BitConverter.GetBytes(number);
            Array.Reverse(temp);
            return BitConverter.ToUInt32(temp, 0);
        }

        /// <summary>
        /// Convert byte arrary to LittleEndian byte array
        /// </summary>
        /// <param name="byteArr"></param>
        /// <returns></returns>
        public static uint LittleEndianConverter(byte[] byteArr)
        {
            Array.Reverse(byteArr);
            return BitConverter.ToUInt32(byteArr, 0);
        }

        public static uint ReadDWord(byte[] byteData, int index, bool needsReverse = true)
        {
            if (needsReverse)
            {
                Array.Reverse(byteData, index, 4);
            }
            return BitConverter.ToUInt32(byteData, index);
        }

        public static byte[] ToDWord(long data)
        {
            byte[] byteData = BitConverter.GetBytes((uint)data);
            Array.Reverse(byteData);
            return byteData; ;
        }

        public static byte[] ToDWord(int data)
        {
            byte[] byteData = BitConverter.GetBytes(data);
            Array.Reverse(byteData);
            return byteData; ;
        }

        public static ushort ReadWord(byte[] byteData, int index, bool needsReverse = true)
        {
            if (needsReverse)
            {
                Array.Reverse(byteData, index, 2);
            }
            return BitConverter.ToUInt16(byteData, index);
        }

        public static short ReadSignWord(byte[] byteData, int index)
        {
            Array.Reverse(byteData, index, 2);
            return BitConverter.ToInt16(byteData, index);
        }

        public static byte[] ToWord(int data)
        {
            byte[] byteData = BitConverter.GetBytes((ushort)data);
            Array.Reverse(byteData);
            return byteData;
        }

        public static byte[] ToWord(ushort data)
        {
            byte[] byteData = BitConverter.GetBytes(data);
            Array.Reverse(byteData);
            return byteData;
        }

        public static byte[] ToBCD6Time(DateTime time)
        {
            byte[] BCD6Time = new byte[6];
            BCD6Time[0] = ToBCDByte(time.Year % 100);
            BCD6Time[1] = ToBCDByte(time.Month);
            BCD6Time[2] = ToBCDByte(time.Day);
            BCD6Time[3] = ToBCDByte(time.Hour);
            BCD6Time[4] = ToBCDByte(time.Minute);
            BCD6Time[5] = ToBCDByte(time.Second);
            return BCD6Time;
        }

        public static DateTime ReadBCD6Time(byte[] byteData, int index)
        {

            int yy = BCDByteToInt(byteData[index++]) + 2000;
            int MM = BCDByteToInt(byteData[index++]);
            int dd = BCDByteToInt(byteData[index++]);
            int HH = BCDByteToInt(byteData[index++]);
            int mm = BCDByteToInt(byteData[index++]);
            int ss = BCDByteToInt(byteData[index]);

            //CJP终端调试临时更新 定位信息汇报时间为 [00 00 00 00 00 00] 造成解析异常
            if (MM == 0)
            {
                MM = 1;
            }
            if (dd == 0)
            {
                dd = 1;
            }

            DateTime time = new DateTime(yy, MM, dd, HH, mm, ss);

            return time;
        }

        public static DateTime BytesToDateTime(byte[] bytes)
        {
            if (bytes != null && bytes.Length == 6)
            {
                long ticks = BitConverter.ToInt64(bytes, 0);
                if (ticks < DateTime.MaxValue.Ticks && ticks > DateTime.MinValue.Ticks)
                {
                    DateTime dt = new DateTime(ticks);
                    return dt;
                }
            }
            return new DateTime();
        }
        public static byte[] DateTimeToBytes(DateTime dt)
        {
            return BitConverter.GetBytes(dt.Ticks);
        }

        /// <summary>
        /// 将时间转换为6字节数组
        /// 用于处理区域设置、路线设置的时间范围设定
        /// 由于消息协议支持周期时间范围，如要限制每天8点30到18点，起始/结束时间
        /// 设为:00-00-00-08-30-00/00-00-00-18-00-00.
        /// 因此假定：如果给定时间的年数值小于等于2000年，则认为是周期时间范围：将yy-MM-dd转为00-00-00
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static byte[] ToBCD6Time2(DateTime time)
        {
            byte[] BCD6Time = new byte[6];
            BCD6Time[0] = ToBCDByte(time.Year % 100);
            BCD6Time[1] = ToBCDByte(time.Month);
            BCD6Time[2] = ToBCDByte(time.Day);
            BCD6Time[3] = ToBCDByte(time.Hour);
            BCD6Time[4] = ToBCDByte(time.Minute);
            BCD6Time[5] = ToBCDByte(time.Second);

            if (time.Year <= 2000)
            {
                BCD6Time[0] = 0;
                BCD6Time[1] = 0;
                BCD6Time[2] = 0;
            }

            return BCD6Time;
        }

        /// <summary>
        /// 将6字节数组转换为时间
        /// 用于处理区域设置、路线设置的时间范围设定
        /// 由于消息协议支持周期时间范围，如要限制每天8点30到18点，起始/结束时间
        /// 设为:00-00-00-08-30-00/00-00-00-18-00-00.
        /// 因此假定：如果转换后时等于0，则说明原来设置的是周期时间范围：00年00月00日转换为2000年01月01日
        /// 即：00-00-00-08-30-00应转为2000-01-01 08:30:00
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static DateTime ReadBCD6Time2(byte[] byteData, int index)
        {
            int yy = BCDByteToInt(byteData[index++]) + 2000;
            int MM = BCDByteToInt(byteData[index++]);
            int dd = BCDByteToInt(byteData[index++]);
            int HH = BCDByteToInt(byteData[index++]);
            int mm = BCDByteToInt(byteData[index++]);
            int ss = BCDByteToInt(byteData[index]);

            if (yy == 2000)
            {
                if (MM == 0) MM = 1;
                if (dd == 0) dd = 1;
            }

            DateTime time = new DateTime(yy, MM, dd, HH, mm, ss);

            return time;
        }

        public static int BCDByteToInt(byte BCDByte)
        {
            return (BCDByte >> 4) * 0xa + (BCDByte & 0xf);
        }

        public static byte ToBCDByte(int data)
        {
            return (byte)(((data / 10) << 4) | (data % 10));
        }

        /// <summary>
        /// Convert String To GBK byte
        /// </summary>
        /// <param name="originStr"></param>
        /// <returns></returns>
        public static byte[] GBKByteConverter(string originStr)
        {
            return Encoding.GetEncoding("GBK").GetBytes(originStr);
        }

        /// <summary>
        /// Get bcd byte array
        /// </summary>
        /// <param name="strMobileNum"></param>
        /// <returns></returns>
        public static byte[] GetBcdArray(string strMobileNum)
        {
            byte[] mobileBcdArray = new byte[6];
            //if (strMobileNum.Length != 12) throw (new Exception("Mobile number's length is not 12"));
            if (strMobileNum.Length < 12)
                strMobileNum = strMobileNum.PadLeft(12, '0');
            else if (strMobileNum.Length > 12)
                strMobileNum = strMobileNum.Substring(0, 12);

            for (int i = 0; i < 6; i++)
            {
                mobileBcdArray[i] = Convert.ToByte(strMobileNum.Substring(i * 2, 2), 16);
            }

            return mobileBcdArray;
        }

        /// <summary>
        /// Retrieve the mobile number
        /// </summary>
        /// <returns></returns>
        public static string GetSmsNumber(byte[] _MobileBcdArray)
        {
            var mobileNumber = new byte[6];
            Array.Copy(_MobileBcdArray, 0, mobileNumber, 0, 6);

            var sbStr = new StringBuilder(12);

            for (var m = 0; m < mobileNumber.Length; m++)
            {
                sbStr.Append(((byte)(mobileNumber[m] >> 4)).ToString() + ((byte)((byte)(mobileNumber[m] << 4) >> 4)).ToString());
            }

            return sbStr.ToString();
        }

        public static uint ReadDWordFrom3Byte(byte[] byteData, int index)
        {
            byte[] buffer = new byte[4] { 0, byteData[index], byteData[index + 1], byteData[index + 2] };
            Array.Reverse(buffer);
            return BitConverter.ToUInt32(buffer, 0);
        }

        public static byte[] To3Byte(long data)
        {
            byte[] buffer = new byte[3];

            byte[] byteData = BitConverter.GetBytes((uint)data);
            //Array.Reverse(byteData);
            buffer[0] = byteData[2];
            buffer[1] = byteData[1];
            buffer[2] = byteData[0];
            return buffer;

        }

        public static string BytesToASCIIString(byte[] byteData, int index, int count)
        {
            string result = string.Empty;
            result = Encoding.ASCII.GetString(byteData, index, count);
            return result;
        }

        /// <summary>
        /// Read BCD5 Time yyMMddHHmm00
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static DateTime ReadBCD5Time(byte[] byteData, int index)
        {
            DateTime time = new DateTime(
                BCDByteToInt(byteData[index++]) + 2000,
                BCDByteToInt(byteData[index++]),
                BCDByteToInt(byteData[index++]),
                BCDByteToInt(byteData[index++]),
                BCDByteToInt(byteData[index++]),
                0
                );

            return time;

        }

        /// <summary>
        /// Read 
        /// </summary>
        /// <param name="byteData"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static DateTime ReadTime_t(byte[] byteData, int index)
        {
            DateTime time = new DateTime();
            return time;
        }

        /// <summary>
        /// Convert date time to bytes
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static byte[] ToTime_t(DateTime time)
        {
            return new byte[8];
        }
        /*
        public static string ReadOctetString(byte[] byteData, int index, int length)
        {
            return Encoding.GetEncoding("GBK").GetString(byteData, index, length).Replace("\0", "");
        }
        */ 

        public static DateTime ReadByte7DateTime(byte[] byteData, int index)
        {
            return new DateTime(
                BitConverter.ToInt16(byteData, index + 2),
                byteData[index + 1],
                byteData[index],
                byteData[index + 4],
                byteData[index + 5],
                byteData[index + 6]
                );
        }


        public static string BytesToHexString(IList<byte[]> iList)
        {
            string result = String.Empty;
            foreach (byte[] msgBytes in iList)
            {
                result += BytesConverter.BytesToHexString(msgBytes) + "\r\n";
            }

            return result;
        }

        /// <summary>
        /// 从字节中按位解析读取值
        /// </summary>
        /// <param name="sourceByte">原始字节</param>
        /// <param name="beginBit">起始bit位 范围0~7</param>
        /// <param name="endBit">结束bit位 范围0~7</param>
        /// <returns></returns>
        public static int ReadBit(byte sourceByte, int beginBit, int endBit)
        {
            BitArray sourceBitArray = new BitArray(new byte[] { sourceByte });
            byte[] resultBytes = new byte[1];
            BitArray resultBitArray = new BitArray(resultBytes);
            for (int i = 0; i < (endBit - beginBit + 1); i++)
            {
                resultBitArray[i] = sourceBitArray[beginBit + i - 1];
            }

            resultBitArray.CopyTo(resultBytes, 0);
            return (int)resultBytes[0];
        }

        /// <summary>
        /// 从一个字节，按7,8 位、5，6位、3,4位、1,2位 解析出4个整数返回
        /// </summary>
        /// <param name="sourceByte"></param>
        /// <returns></returns>
        public static int[] GetIntByBit(byte sourceByte)
        {
            int[] resultIntArray = new int[4];//返回值

            BitArray sourceBitArray = new BitArray(new byte[] { sourceByte });//源数据bit数组           
            BitArray tmpBitArray = new BitArray(8, false);
            byte[] tmpBytes = new byte[1];

            //读出 7、8位
            tmpBitArray[6] = sourceBitArray[6];
            tmpBitArray[7] = sourceBitArray[7];
            tmpBitArray.CopyTo(tmpBytes, 0);
            resultIntArray[0] = (int)tmpBytes[0];
            tmpBitArray.SetAll(false);

            //读出 5、6位
            tmpBitArray[4] = sourceBitArray[4];
            tmpBitArray[5] = sourceBitArray[5];
            tmpBitArray.CopyTo(tmpBytes, 0);
            resultIntArray[1] = (int)tmpBytes[0];
            tmpBitArray.SetAll(false);

            //读出 3、4位
            tmpBitArray[2] = sourceBitArray[2];
            tmpBitArray[3] = sourceBitArray[3];
            tmpBitArray.CopyTo(tmpBytes, 0);
            resultIntArray[2] = (int)tmpBytes[0];
            tmpBitArray.SetAll(false);

            //读出 1、2位
            tmpBitArray[1] = sourceBitArray[1];
            tmpBitArray[0] = sourceBitArray[0];
            tmpBitArray.CopyTo(tmpBytes, 0);
            resultIntArray[3] = (int)tmpBytes[0];

            return resultIntArray;
        }


        public static byte GetByteFromString(int? status4, int? status3, int? status2, int? status1)
        {
            status4 = status4.HasValue ? status4 : 0;
            status3 = status3.HasValue ? status3 : 0;
            status2 = status2.HasValue ? status2 : 0;
            status1 = status1.HasValue ? status1 : 0;
            string oldString = toFullBinaryString(status4.Value) + toFullBinaryString(status3.Value) + toFullBinaryString(status2.Value) + toFullBinaryString(status1.Value);
            return (byte)Convert.ToInt32(oldString, 2);
        }
        public static String toFullBinaryString(int num)
        {
            char[] chs = new char[2];
            for (int i = 0; i < 2; i++)
            {
                chs[1 - i] = (char)(((num >> i) & 1) + '0');
            }
            return new String(chs);
        }

        /// <summary>
        /// 计算32位整型中有多少位是1
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static uint CountBits(uint x)
        {
            // count bits of each 2-bit chunk
            x = x - ((x >> 1) & 0x55555555);
            // count bits of each 4-bit chunk
            x = (x & 0x33333333) + ((x >> 2) & 0x33333333);

            // count bits of each 8-bit chunk
            x = x + (x >> 4);
            // mask out junk
            x &= 0xF0F0F0F;
            // add all four 8-bit chunks
            return (x * 0x01010101) >> 24;
        }

        /// <summary>
        /// 根据给定的字节数组，返回对应的GBK格式字符串
        /// </summary>
        /// <param name="sourceByte"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string GetGBKString(byte[] sourceByte, int index, int count)
        {
            return Encoding.GetEncoding("GBK").GetString(sourceByte, index, count);//透传消息内容
        }

        public static string ConvertDateTimeToString(DateTime? time)
        {
            if (time.HasValue)
            {
                return time.Value.ToString("yyyy-MM-dd HH:mm:ss");
            }
            else
            {
                return "--";
            }
        }
        public static string ConvertDateTimeToString(DateTime? time, string stringFormat)
        {
            if (time.HasValue)
            {
                return time.Value.ToString(stringFormat);
            }
            else
            {
                return string.Empty;
            }
        }
    }
    public enum ByteType
    {
        BYTE_FIVE = 5,
        BCD = 6,
        BYTE_SEVEN = 7,
        BYTE_EIGHT = 8,
        BYTE_TWENTY = 20,
        BYTE_TWENTYONE = 21,
        BYTE_KEY = 128,
        BYTE_ANY = 0
    }
}
